﻿using System;
using System.Security.Cryptography;
using System.Text;

namespace SharpDepend.Shared
{
    internal class AESSecurityManager : IAESSecurityManager
    {
        internal class AESSecurity : IAESSecurity
        {
            private AesManaged _rcipher;
            private byte[] _key, _pwd, _ivBytes, _iv;
            private string mKey;
            private int mKeySize;

            private enum EncryptMode { ENCRYPT, DECRYPT };

            static readonly char[] CharacterMatrixForRandomIVStringGeneration = {
			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
			'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 
			'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
			'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 
			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
		};

            public void SetKey(string key, int size)
            {
                mKey = key;
                mKeySize = size;

                _rcipher.KeySize = mKeySize * 8;
                _key = new byte[mKeySize];
            }

            public string InitializeVector { get; set; }

            public AESSecurity()
            {
                _rcipher = new AesManaged();
                _rcipher.BlockSize = 128;
                _iv = new byte[_rcipher.BlockSize / 8];
                _ivBytes = new byte[16];
            }

            public void GenerateInitializeVector(int length)
            {
                char[] _iv = new char[length];
                byte[] randomBytes = new byte[length];
                
                RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
                {
                    rng.GetBytes(randomBytes);
                }
                
                for (int i = 0; i < _iv.Length; i++)
                {
                    int ptr = randomBytes[i] % CharacterMatrixForRandomIVStringGeneration.Length;
                    _iv[i] = CharacterMatrixForRandomIVStringGeneration[ptr];
                }
                
                InitializeVector = new string(_iv);
            }

            public static string GetHashSha256(string text, int length)
            {
                byte[] bytes = Encoding.UTF8.GetBytes(text);
                using (SHA256Managed hashstring = new SHA256Managed())
                {
                    byte[] hash = hashstring.ComputeHash(bytes);
                    string hashString = string.Empty;
                    foreach (byte x in hash)
                    {
                        hashString += String.Format("{0:x2}", x); //covert to hex string
                    }
                    if (length > hashString.Length)
                        return hashString;
                    else
                        return hashString.Substring(0, length);
                }
            }

            private byte[] encryptDecrypt(byte[] _inputData, string _encryptionKey, EncryptMode _mode, string _initVector)
            {
                byte[] _out = null;// output string
                _pwd = Encoding.UTF8.GetBytes(_encryptionKey);
                _ivBytes = Encoding.UTF8.GetBytes(_initVector);

                int len = _pwd.Length;
                if (len > _key.Length)
                {
                    len = _key.Length;
                }
                int ivLenth = _ivBytes.Length;
                if (ivLenth > _iv.Length)
                {
                    ivLenth = _iv.Length;
                }

                Array.Copy(_pwd, _key, len);
                Array.Copy(_ivBytes, _iv, ivLenth);
                _rcipher.Key = _key;
                _rcipher.IV = _iv;

                if (_mode.Equals(EncryptMode.ENCRYPT))
                {
                    //encrypt
                    using (ICryptoTransform cd = _rcipher.CreateEncryptor())
                    {
                        byte[] dd = _inputData;
                        byte[] plainText = cd.TransformFinalBlock(dd, 0, dd.Length);
                        _out = plainText;
                    }
                }
                if (_mode.Equals(EncryptMode.DECRYPT))
                {
                    //decrypt

                    using (ICryptoTransform cd = _rcipher.CreateDecryptor())
                    {
                        byte[] dd = _inputData;
                        byte[] plainText = cd.TransformFinalBlock(dd, 0, dd.Length);
                        _out = plainText;
                    }
                }

                return _out;// return encrypted/decrypted string
            }

            public byte[] Decrypt(byte[] data)
            {
                return encryptDecrypt(data, mKey, EncryptMode.DECRYPT, InitializeVector);
            }

            public byte[] Encrypt(byte[] data)
            {
                return encryptDecrypt(data, mKey, EncryptMode.ENCRYPT, InitializeVector);
            }
        }

        public IAESSecurity CreateAESSecurity()
        {
            return new AESSecurity();
        }

        public string GetHashSha256(string key, int size)
        {
            return AESSecurity.GetHashSha256(key, size);
        }
    }
}
